package org.qpcr.community.standalone.authorize;

import org.hswebframework.web.authorization.events.AuthorizationFailedEvent;
import org.hswebframework.web.authorization.exception.AuthenticationException;
import org.hswebframework.web.system.authorization.api.entity.UserEntity;
import org.hswebframework.web.system.authorization.api.service.reactive.ReactiveUserService;
import org.springframework.context.event.EventListener;
import org.springframework.data.redis.core.ReactiveRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * 处理登录失败的服务
 */
@Component
public class LoginFailureHandler {

    private final ReactiveUserService userService;
    private final ReactiveRedisTemplate<String, String> redisTemplate;
    private static final String LOGIN_FAILURE_KEY_PREFIX = "login:failure:";
    private static final int MAX_FAILURE_COUNT = 5;
    private static final String FIRST_LOGIN_STATUS = "40001";

    public LoginFailureHandler(ReactiveRedisTemplate<String, String> redisTemplate,
                             ReactiveUserService userService) {
        this.redisTemplate = redisTemplate;
        this.userService = userService;
    }

    @EventListener
    public void handleLoginFailure(AuthorizationFailedEvent event) {
        String username = event.getUsername();
        String key = LOGIN_FAILURE_KEY_PREFIX + username;

        // 检查是否是首次登录需要修改密码的情况
        if (event.getException() instanceof AuthenticationException) {
            AuthenticationException authEx = (AuthenticationException) event.getException();
            if (FIRST_LOGIN_STATUS.equals(authEx.getCode())) {
                // 如果是首次登录需要修改密码，不增加失败次数
                return;
            }
        }

        // 增加失败次数
        redisTemplate.opsForValue()
                .increment(key)
                .flatMap(count -> {
                    if (count >= MAX_FAILURE_COUNT) {
                        // 禁用账号
                        return userService.findByUsername(username)
                                .flatMap(user -> {
                                    if (user != null && !"admin".equals(username)) {
                                        return userService.changeState(Mono.just(user.getId()), (byte) 0)
                                                .then(redisTemplate.delete(key));
                                    }
                                    return Mono.empty();
                                });
                    }
                    // 设置过期时间（24小时）
                    return redisTemplate.expire(key, Duration.ofHours(24));
                })
                .subscribe();
    }

    /**
     * 重置用户的登录失败次数
     */
    public Mono<Void> resetLoginFailureCount(String username) {
        String failureKey = LOGIN_FAILURE_KEY_PREFIX + username;
        return redisTemplate.delete(failureKey).then();
    }
}